home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / C and C++ / Miscellaneous / ival / score.c < prev   
Encoding:
C/C++ Source or Header  |  1987-05-25  |  22.1 KB  |  904 lines  |  [TEXT/EDIT]

  1. /*
  2.  * score.c - routines for drawing a PICT-based music score on the screen.
  3.  */
  4.  
  5. #include <quickdraw.h>
  6. #include <memory.h>
  7. #include <window.h>
  8. #include <resource.h>
  9. #include <control.h>
  10. #define ALLDIM 255    /* should be in control.h, but isn't    */
  11. #include <event.h>
  12. #include <desk.h>
  13. #include <font.h>
  14.  
  15. #include "ival.h"
  16.  
  17. /*
  18.  * SCORESPACES - total number of spaces per score:
  19.  *   4 for each staff (treble and bass);
  20.  *   2 between the staves;
  21.  *   2 above the treble + 2 below the bass;
  22.  *   1 above and 1 below for margins.
  23.  */
  24.  
  25. #define SCORESPACES    16    /* total number of spaces per score    */
  26.  
  27. /*
  28.  * nobuts[] - buttons for the "Notation" window pane
  29.  */
  30.  
  31. #define NOSTRS        137    /* STR#    */
  32. #define NO_GIVE         0    /* "Give Notation"    */
  33. #define NO_COUNT     1
  34. static ControlHandle nobuts[NO_COUNT];
  35.  
  36. /*
  37.  * odegofdeg() - get the scale degree of an absolute degree.
  38.  *   Given a degree on the staff, return the degree within the octave.
  39.  * NOTE: since staff degrees are relative to low C,
  40.  *   scale degrees go from C up to B rather than A to G.
  41.  */
  42.  
  43. #define odegofdeg(d) (d % DEGPEROCT)
  44.  
  45. /*
  46.  * treb/bass-flats/sharps[] - arrays of accidentals in the key signature.
  47.  * Given in the order of # of flats/sharps in the key, each element gives
  48.  * the degree (relative to low C) of that flat or sharp in the key
  49.  * signature.
  50.  */
  51.  
  52. short trebflats[DEGPEROCT] = {
  53.     20,        /* Bb    */
  54.     23,        /* Eb    */
  55.     19,        /* Ab    */
  56.     22,        /* Db    */
  57.     18,        /* Gb    */
  58.     21,        /* Cb    */
  59.     17        /* Fb    */
  60. };
  61. short bassflats[DEGPEROCT] = {
  62.      6,        /* Bb    */
  63.      9,        /* Eb    */
  64.      5,        /* Ab    */
  65.      8,        /* Db    */
  66.      4,        /* Gb    */
  67.      7,        /* Cb    */
  68.      3        /* Fb    */
  69. };
  70. short trebsharps[DEGPEROCT] = {
  71.     24,        /* F#    */
  72.     21,        /* C#    */
  73.     25,        /* G#    */
  74.     22,        /* D#    */
  75.     19,        /* A#    */
  76.     23,        /* E#    */
  77.     20        /* B#    */
  78. };
  79. short basssharps[DEGPEROCT] = {
  80.     10,        /* F#    */
  81.      7,        /* C#    */
  82.     11,        /* G#    */
  83.      8,        /* D#    */
  84.      5,        /* A#    */
  85.      9,        /* E#    */
  86.      6        /* B#    */
  87. };
  88.  
  89. /*
  90.  * (local to iniacctab(), addacc() and accoftone().)
  91.  *
  92.  * acctab[] - array of accidentals implied in this measure so far.
  93.  *   Acctab[] records, for each scale degree, the current implied
  94.  *   accidental (if any) associated with that degree.
  95.  * Indexed by the result of odegofdeg(), each acctab entry contains
  96.  * a CH_* value.
  97.  */
  98.  
  99. short acctab[DEGPEROCT];
  100.  
  101. /*
  102.  * (local to notemouse() and unnote())
  103.  *
  104.  * mousetone - the (very temporary) note tracking the mouse.
  105.  * mtonedrawn - "The mouse tone has been drawn onto the screen."
  106.  * mousebits - a copy of what the screen looked like before the mousetone
  107.  *  was drawn.  NOTE: mousebits is valid only if mtonedrawn is TRUE.
  108.  */
  109.  
  110. struct atone mousetone;
  111. int mtonedrawn = FALSE;
  112. BitMap mousebits;
  113.  
  114. /*
  115.  * chromedrag - TRUE if the mouse is dragging the chromatic adjustment
  116.  * rather than the degree of a note.
  117.  */
  118.  
  119. int chromedrag;
  120.  
  121. /*
  122.  * *rect - Rect's enclosing the areas of the score.
  123.  * Each of these rects should cover a vertical band of the score.
  124.  */
  125.  
  126. static Rect clefrect;    /* clefs are left-justified into this rect    */
  127. static Rect keyrect;    /* the key accidentals are left-just'ed        */
  128. static Rect tonerect[NUMTONES];    /* accidental is left, note is next    */
  129. static short vmidc;    /* vertical coord of middle C            */
  130.  
  131. /*
  132.  * spaceheight - the (pixels) height of one space on the score
  133.  * I.E., the distance from one line down to (but not including) the next.
  134.  */
  135.  
  136. static short spaceheight;
  137.  
  138. #define MAXPICTS 4    /* max number of objects in a group + 1    */
  139.  
  140. struct pictinfo {
  141.     short resid[MAXPICTS];    /* resID's of the picts (0 == list end)    */
  142.     BitMap *bits[MAXPICTS];    /* pointers to the bitmaps        */
  143.     short width;        /* pixels width of the widest pict    */
  144.     short vref;            /* vertical distance from top to "zero"    */
  145. };
  146.  
  147. /*
  148.  * CLEF*, clefpict - Clef (Bass and Treble) picture information.
  149.  */
  150.  
  151. #define CLEFTREB    0    /* clefpict[] index of Treble clef    */
  152. #define CLEFBASS    1    /* ditto for Bass clef            */
  153. #define CLEFNUM        2    /* number of clefpict[] entries        */
  154. static struct pictinfo clefpict = {
  155.     { 129, 130, 0}        /* resID's of clefs            */
  156. };
  157.  
  158. /*
  159.  * ACC*, accpict - Accidentals' (flat, natural, sharp) pict info.
  160.  */
  161.  
  162. #define ACCADJ    (-CH_FLAT)    /* conversion from CH_* to accpict[]    */
  163. #define ACCNONE    (-1)            /* accpict[] idx code for no ACC*/
  164. #define ACCFLAT    (ACCADJ + CH_FLAT)    /* accpict[] idx of flat    */
  165. #define ACCNAT    (ACCADJ + CH_NAT)    /* " " a natural        */
  166. #define ACCSHARP (ACCADJ + CH_SHARP)    /* " " a sharp            */
  167. #define ACCNUM        3    /* number of accpict[] entries        */
  168. static struct pictinfo accpict = {
  169.     { 131, 132, 133, 0}        /* resID's of accidentals        */
  170. };
  171.  
  172. /*
  173.  * updurpict, dndurpict - Note durations' (whole note only) pict info.
  174.  * Each note duration is represented once in each pict:
  175.  * once pointing stem-up; once, stem-down.
  176.  */
  177.  
  178. #define DURWHOLE    0    /* *durpict[] index of a whole note    */
  179. #define DURNUM        1    /* number of *durpict[] entries        */
  180. static struct pictinfo updurpict = {
  181.     { 134, 0}        /* resID's of stem up versions of notes    */
  182. };
  183. static struct pictinfo dndurpict = {
  184.     { 135, 0}        /* resID's of stem-down versions    */
  185. };
  186.  
  187. /*
  188.  * getscore() - initialize the score element PICT info structures.
  189.  */
  190.  
  191. getscore()
  192. {
  193.     short oldfont;    /* original font of this port            */
  194.     Point delta;    /* amount to offset each rect            */
  195.     short numstrs;    /* number of strings in the button labels    */
  196.     short width;    /* a width (in pixels)                */
  197.     Rect tmprect;    /* a temporary variable                */
  198.     short i;
  199.     
  200.     /*
  201.      * Get the pictures and their widths,
  202.      * Then find the line spacing and the vertical references.
  203.      * (See the MacPaint picture prototypes to understand the
  204.      * values for vertical references.)
  205.      */
  206.     
  207.     paw(&clefpict);
  208.     paw(&accpict);
  209.     paw(&updurpict);
  210.     paw(&dndurpict);
  211.     
  212.     for (i = 0; i < CLEFNUM; ++i) {
  213.     cvrandspace(clefpict.bits[i], &clefpict.vref);
  214.     }
  215.     
  216.     /*
  217.      * NOTE: vref's measure from the top-down, so they round
  218.      * spaceheight / 2 down rather than rounding up.
  219.      */
  220.  
  221.     accpict.vref = spaceheight + spaceheight / 2;
  222.     updurpict.vref = 3 * spaceheight + spaceheight / 2;
  223.     dndurpict.vref = spaceheight + spaceheight / 2;
  224.     
  225.     /*
  226.      * find the score rectangle,
  227.      * then split that rectangle into the necessary vertical bands.
  228.      */
  229.  
  230.     scorerect.top = 0;
  231.     scorerect.left = 0;
  232.     scorerect.bottom = scorerect.top + SCORESPACES * spaceheight;
  233.     vmidc = scorerect.top + SCORESPACES / 2 * spaceheight;
  234.     
  235.     clefrect.top = scorerect.top;
  236.     clefrect.bottom = scorerect.bottom;
  237.     clefrect.left = scorerect.left;
  238.     clefrect.right = clefrect.left + clefpict.width;
  239.     
  240.     keyrect.top = scorerect.top;
  241.     keyrect.bottom = scorerect.bottom;
  242.     keyrect.left = clefrect.right;
  243.     keyrect.right = keyrect.left + DEGPEROCT * accpict.width;
  244.     
  245.     tonerect[0].top = scorerect.top - 5;    /* "5" is a hack for accidentals */
  246.     tonerect[0].bottom = scorerect.bottom + 5;
  247.     tonerect[0].left = keyrect.right + updurpict.width;
  248.     tonerect[0].right = tonerect[0].left + accpict.width + updurpict.width
  249.       + updurpict.width / 4;
  250.     
  251.     for (i = 1; i < NUMTONES; ++i) {
  252.     tonerect[i].top = tonerect[i - 1].top;
  253.     tonerect[i].bottom = tonerect[i - 1].bottom;
  254.     tonerect[i].left = tonerect[i - 1].right + updurpict.width / 4;
  255.     tonerect[i].right = tonerect[i].left +
  256.       accpict.width + updurpict.width + updurpict.width / 4;
  257.     }
  258.  
  259.     /*
  260.      * Now that the Rects are placed relative to zero,
  261.      * move the score structures so that they are centered within the pane.
  262.      */
  263.     
  264.     scorerect.right = tonerect[NUMTONES - 1].right + updurpict.width / 4;
  265.     delta.h = (noterect.left + noterect.right) / 2 -
  266.       (scorerect.left + scorerect.right) / 2;
  267.     delta.v = (noterect.top + noterect.bottom - BUTHEIGHT) / 2 -
  268.       (scorerect.top + scorerect.bottom) / 2;
  269.     
  270.     OffsetRect(&scorerect, delta.h, delta.v);
  271.     vmidc = scorerect.top + SCORESPACES / 2 * spaceheight;
  272.  
  273.     OffsetRect(&clefrect, delta.h, delta.v);
  274.     OffsetRect(&keyrect, delta.h, delta.v);
  275.     for (i = 0; i < NUMTONES; ++i) {
  276.     OffsetRect(&tonerect[i], delta.h, delta.v);
  277.     }
  278.     
  279.     /*
  280.      * put this pane's control buttons against the left side of the pane.
  281.      */
  282.  
  283.     oldfont = thePort->txFont;
  284.     TextFont(BUTTONFONT);
  285.     width = strswidth(NOSTRS, &numstrs) + BUTEXTRAH;
  286.     tmprect.left = noterect.left + BUTHMARG;
  287.     tmprect.right = tmprect.left + width;
  288.     tmprect.bottom = noterect.bottom;
  289.     tmprect.top = tmprect.bottom - BUTHEIGHT;
  290.     delta.h = width + BUTHMARG;
  291.     delta.v = 0;
  292.     makeradcol(nobuts, BUTPROCID, NOSTRS, &tmprect, delta.h, delta.v);
  293.     
  294.     TextFont(oldfont);
  295. }
  296.  
  297. /*
  298.  * newtones() - reset the score controls, based on the new test state.
  299.  */
  300.  
  301. newtones()
  302. {
  303.     /*
  304.      * Hilite or Dim the "Give" control based on whether we give the 2nd tone
  305.      */
  306.  
  307.     if (!progtasks.notate) {
  308.     HiliteControl(nobuts[NO_GIVE], 0);
  309.     tone1shown = FALSE;
  310.     } else {
  311.     HiliteControl(nobuts[NO_GIVE], ALLDIM);
  312.     tone1shown = TRUE;
  313.     }
  314. }
  315.  
  316. /*
  317.  * pickkey() - choose a random new value for the current key, curkey.
  318.  */
  319.  
  320. pickkey()
  321. {
  322.     short i;
  323.  
  324.     curkey.insharps = randint(2);    /* 0 = FALSE, 1 = TRUE */
  325.     curkey.number = randint(DEGPEROCT + 1);
  326.  
  327.     InvalRect(&keyrect);
  328.     for (i = 0; i < NUMTONES; ++i) {
  329.     InvalRect(&tonerect[i]);    /* (accidentals may have changed)*/
  330.     }
  331. }
  332.  
  333. /*
  334.  * picktone() - choose a random pitch for the given tone.
  335.  */
  336.  
  337. picktone(tidx)
  338. short tidx;    /* index of the tone to pick    */
  339. {
  340.     curtone[tidx].degree = randint(DEGPERSCORE);
  341.     curtone[tidx].chrome = randint(NUM_CH) + CH_FLAT;
  342.     InvalRect(&tonerect[tidx]);
  343. }
  344.  
  345. /*
  346.  * notepress() - handle a button-press in the Note pane.
  347.  */
  348.  
  349. notepress(whichcontrol)
  350. ControlHandle whichcontrol;
  351. {
  352.     /* it must be "give" */
  353.  
  354.     sawtone1();
  355.  
  356. }
  357.  
  358. /*
  359.  * sawtone1() - note that the second tone has been seen.
  360.  */
  361.  
  362. sawtone1()
  363. {
  364.     HiliteControl(nobuts[NO_GIVE], ALLDIM);
  365.     tone1shown = TRUE;
  366.     InvalRect(&scorerect);
  367. }
  368.  
  369. /*
  370.  * drawscore() - draw the score within its window.
  371.  * Assumes: thePort == windoc.
  372.  */
  373.  
  374. drawscore()
  375. {
  376.     short n;
  377.     Rect tmprect;
  378.  
  379.     drawstaff(vmidc - 5 * spaceheight);
  380.     drawstaff(vmidc + spaceheight);
  381.     drawbits(clefpict.bits[CLEFTREB], clefrect.left,
  382.       vmidc - 5 * spaceheight - clefpict.vref);
  383.     drawbits(clefpict.bits[CLEFBASS], clefrect.left,
  384.       vmidc + spaceheight - clefpict.vref);
  385.     drawkey(&curkey);
  386.     iniacctab();
  387.     for (n = 0; n < NUMTONES; ++n) {
  388.     if (n != 1 || tone1shown) {
  389.         drawtone(n, &curtone[n]);
  390.         addacc(&curtone[n]);
  391.     }
  392.     }
  393. }
  394.  
  395. /*
  396.  * drawstaff() - draw a scorerect-wide staff at the given vertical.
  397.  */
  398.  
  399. drawstaff(topv)
  400. short topv;    /* vertical coord of the top of the staff        */
  401. {
  402.     short v;
  403.  
  404.     MoveTo(scorerect.left, topv);
  405.     LineTo(scorerect.left, topv + 4 * spaceheight);
  406.     
  407.     for (v = topv; v <= topv + 4 * spaceheight; v += spaceheight) {
  408.     MoveTo(scorerect.left, v);
  409.     LineTo(scorerect.right - 1, v);
  410.     }
  411. }
  412.  
  413. /*
  414.  * drawkey() - draw the given key in the keyrect.
  415.  */
  416.  
  417. drawkey(kp)
  418. struct akey *kp;    /* the key to draw    */
  419. {
  420.     short *list;    /* current list of flats/sharps        */
  421.     BitMap *bmp;    /* the sharp/flat bitmap        */
  422.     short i;
  423.     short h;        /* horz coord of the current flat/sharp    */
  424.     
  425.     if (kp->insharps) {
  426.     list = &trebsharps[0];
  427.     bmp = accpict.bits[ACCSHARP];
  428.     } else {
  429.     list = &trebflats[0];
  430.     bmp = accpict.bits[ACCFLAT];
  431.     }
  432.     for (i = 0, h = keyrect.left; i < kp->number;
  433.       ++i, h += accpict.width) {
  434.     drawbits(bmp, h, degtov(list[i]) - accpict.vref);
  435.     }
  436.     
  437.     if (kp->insharps) {
  438.     list = &basssharps[0];
  439.     } else {
  440.     list = &bassflats[0];
  441.     }
  442.     for (i = 0, h = keyrect.left; i < kp->number;
  443.       ++i, h += accpict.width) {
  444.     drawbits(bmp, h, degtov(list[i]) - accpict.vref);
  445.     }
  446. }
  447.  
  448. drawtone(tidx, tp)
  449. short tidx;        /* index of the tone to draw            */
  450. struct atone *tp;    /* the tone to draw                */
  451. {
  452.     short degv;            /* vertical of this degree        */
  453.     short acc;            /* the accidental index (if any)    */
  454.     short degl;            /* degree of this ledger-line        */
  455.  
  456.     degv = degtov(tp->degree);
  457.     acc = accoftone(tp);
  458.  
  459.     if (acc != ACCNONE) {
  460.     drawbits(accpict.bits[acc], tonerect[tidx].left,
  461.       degv - accpict.vref);
  462.     }
  463.     drawbits(dndurpict.bits[DURWHOLE],
  464.       tonerect[tidx].left + accpict.width, degv - dndurpict.vref);
  465.  
  466.     /*
  467.      * draw the necessary ledger-lines
  468.      */
  469.  
  470.     if (tp->degree < DEG_LOWF) {
  471.     for (degl = DEG_LOWF - 1; degl >= tp->degree; degl -= 2) {
  472.         drawledger(tidx, degl);
  473.     }
  474.     } else if (tp->degree == DEG_MIDC) {
  475.     drawledger(tidx, tp->degree);
  476.     } else if (tp->degree > DEG_HIGG) {
  477.     for (degl = DEG_HIGG + 1; degl <= tp->degree; degl += 2) {
  478.         drawledger(tidx, degl);
  479.     }
  480.     }
  481. }
  482.  
  483. /*
  484.  * drawledger() - draw a ledger-line at the given degree
  485.  *  and within the given tone's rectangle.
  486.  */
  487.  
  488. drawledger(tidx, ldeg)
  489. short tidx;        /* index of the tone this line is drawn for    */
  490. short ldeg;        /* degree to draw the line at            */
  491. {
  492.     short margin;
  493.     
  494.     margin = updurpict.width / 4;
  495.     MoveTo(tonerect[tidx].left + accpict.width - margin, degtov(ldeg));
  496.     Line(margin + updurpict.width + margin - 1, 0);
  497. }
  498.  
  499. /*
  500.  * notemouse() - update the placement of a note, based on the mouse position
  501.  * (usually called between events).
  502.  */
  503.  
  504. notemouse(ptp)
  505. Point *ptp;    /* where the mouse is (in some local coords)    */
  506. {
  507.     GrafPtr saveport;
  508.     Point where;        /* windoc coords of the mouse position    */
  509.     struct atone newtone;    /* mouse coords interpreted as a tone    */
  510.     int newdrawn;        /* "the new tone should be drawn"    */
  511.     short vtodeg();
  512.     short dvtochrome();
  513.  
  514.     /*
  515.      * Select our window, converting the mouse point as we do it.
  516.      */
  517.  
  518.     GetPort(&saveport);
  519.     where = *ptp;
  520.     LocalToGlobal(&where);
  521.     SetPort(windoc);
  522.     GlobalToLocal(&where);
  523.  
  524.     /*
  525.      * We draw a tone only if:
  526.      *   The second hasn't already been given;
  527.      *   our window is selected;
  528.      *   the mouse is inside the score.
  529.      */
  530.  
  531.     newdrawn = !tone1shown && (saveport == windoc)
  532.       && PtInRect(pass(where), &scorerect);
  533.  
  534.     if (newdrawn) {
  535.     if (chromedrag) {
  536.  
  537.         /*
  538.          * dragging the chromatic adjustment,
  539.          * we assume the mousetone.degree has already been set
  540.          * by a recent call to notemouse().
  541.          */
  542.  
  543.         newtone.degree = mousetone.degree;
  544.         newtone.chrome = dvtochrome(newtone.degree, where.v);
  545.     } else {
  546.         newtone.degree = vtodeg(where.v);
  547.         newtone.chrome = chromeofdeg(newtone.degree);
  548.     }
  549.     }
  550.  
  551.     /*
  552.      * If things haven't changed, we're done.
  553.      */
  554.  
  555.     if (newdrawn == mtonedrawn && (!mtonedrawn ||
  556.       (mousetone.degree == newtone.degree &&
  557.        mousetone.chrome == newtone.chrome))) {
  558.     SetPort(saveport);
  559.     return;
  560.     }
  561.  
  562.     /*
  563.      * If there was a previous mouse tone,
  564.      * remove it by copying the blank score onto it.
  565.      */
  566.  
  567.     if (mtonedrawn) {
  568.     CopyBits(&mousebits, &(thePort->portBits),
  569.       &(mousebits.bounds), &tonerect[1], srcCopy, (RgnHandle) 0);
  570.     }
  571.  
  572.     /*
  573.      * If we are to draw a new tone, do it:
  574.      *   Save the underlying bits (this step initializes mousebits);
  575.      *   Draw the new tone.
  576.      */
  577.  
  578.  
  579.     mtonedrawn = newdrawn;
  580.     if (mtonedrawn) {
  581.     mousetone = newtone;
  582.     }
  583.     if (mtonedrawn) {
  584.     if (!mousebits.baseAddr) {
  585.         topLeft(mousebits.bounds) = topLeft(tonerect[1]);
  586.         botRight(mousebits.bounds) = botRight(tonerect[1]);
  587.         mousebits.rowBytes =
  588.           (mousebits.bounds.right - mousebits.bounds.left + 7) / 8;
  589.         if (mousebits.rowBytes & 1) ++mousebits.rowBytes;
  590.         mousebits.baseAddr = (QDPtr) NewPtr((Ptr) (mousebits.rowBytes *
  591.           (mousebits.bounds.bottom - mousebits.bounds.top)));
  592.     }
  593.     CopyBits(&(thePort->portBits), &mousebits, &tonerect[1], &mousebits.bounds,
  594.       srcCopy, (RgnHandle) 0);
  595.     drawtone(1, &mousetone);
  596.     }
  597.  
  598.     SetPort(saveport);
  599. }
  600.  
  601. /*
  602.  * drawmouse() - draw the current state of the mouse note.
  603.  * This routine is called only when redrawing the whole window.
  604.  * NOTE: this routine assumes the port is set to windoc.
  605.  */
  606.  
  607. drawmouse()
  608. {
  609.     if (mtonedrawn) {
  610.     CopyBits(&mousebits, &(thePort->portBits), &(mousebits.bounds), &tonerect[1],
  611.       srcCopy, (RgnHandle) 0);
  612.     drawtone(1, &mousetone);
  613.     }
  614. }
  615.  
  616. /*
  617.  * unnote() - remove the current note drawn by notemouse().
  618.  */
  619.  
  620. unnote()
  621. {
  622.     GrafPtr saveport;
  623.  
  624.     if (!mtonedrawn) return;
  625.     GetPort(&saveport);
  626.     SetPort(windoc);
  627.  
  628.     CopyBits(&mousebits, &(thePort->portBits), &(mousebits.bounds), &tonerect[1],
  629.       srcCopy, (RgnHandle) 0);
  630.  
  631.     mtonedrawn = FALSE;
  632.     SetPort(saveport);
  633. }
  634.  
  635. /*
  636.  * dropnote() - place a note where the mouse was pressed
  637.  */
  638.  
  639. dropnote(ptp)
  640. Point *ptp;    /* mouse-down point    */
  641. {
  642.     struct atone mtone;        /* the tone associated with the mouse    */
  643.  
  644.     if (tone1shown || !PtInRect(pass(*ptp), &scorerect)) return;
  645.  
  646.     mtone.degree = vtodeg(ptp->v);
  647.     mtone.chrome = chromeofdeg(mtone.degree);
  648.  
  649.     notemouse(ptp);
  650.     chromedrag = TRUE;
  651.     while (StillDown()) {
  652.     SystemTask();
  653.     GetMouse(ptp);
  654.     notemouse(ptp);
  655.     }
  656.     mtone.chrome = dvtochrome(mtone.degree, ptp->v);
  657.     chromedrag = FALSE;
  658.     unnote();
  659.  
  660.     /*
  661.      * If the mouse was released outside the score, treat it as a 'cancel'.
  662.      * Otherwise, If the user got the note right, mark it.
  663.      */
  664.  
  665.     if (!PtInRect(pass(*ptp), &scorerect)) {
  666.     return;
  667.     }
  668.     if (mtone.degree == curtone[1].degree && mtone.chrome == curtone[1].chrome) {
  669.     sawtone1();
  670.     }
  671. }
  672.  
  673. /*
  674.  * accoftone() - given a tone, return the accidental to draw for that
  675.  *  tone, based on what tones have been registered via addacc()  since the last
  676.  *  iniacctab() call.
  677.  */
  678.  
  679. int    /* ACC* of the tone    */
  680. accoftone(tp)
  681. struct atone *tp;    /* the tone to analyze        */
  682. {
  683.     short odeg;        /* the tone's degree within an octave    */
  684.     short chrome;    /* the assumed chromatic of this degree    */
  685.     
  686.     odeg = odegofdeg(tp->degree);
  687.     if (tp->chrome == acctab[odeg]) {
  688.     return(ACCNONE);
  689.     }
  690.     return(ACCADJ + tp->chrome);
  691. }
  692.  
  693. /*
  694.  * iniacctab() - reset the accidental table, based on the current key.
  695.  */
  696.  
  697. iniacctab()
  698. {
  699.     short *list;    /* current list of flats/sharps        */
  700.     short odeg;        /* degree within an octave        */
  701.     short i;
  702.     short chrome;    /* chromatic modification to use    */
  703.     
  704.     for (odeg = 0; odeg < DEGPEROCT; ++odeg) {
  705.     acctab[odeg] = chromeofdeg(odeg);
  706.     }
  707. }
  708.  
  709. /*
  710.  * addacc() - given a tone, add it's chromatic change to the current
  711.  * set of accidentals.
  712.  */
  713.  
  714. addacc(tp)
  715. struct atone *tp;
  716. {
  717.     short odeg;        /* the tone's degree within an octave    */
  718.     short chrome;    /* the assumed chromatic of this degree    */
  719.     
  720.     odeg = odegofdeg(tp->degree);
  721.     acctab[odeg] = tp->chrome;
  722. }
  723.  
  724. /*
  725.  * chromeofdeg() - given any degree,
  726.  * chromeofdeg() returns the chromatic adjustment of that note
  727.  * based only on the current key signature.
  728.  */
  729.  
  730. int
  731. chromeofdeg(deg)
  732. short deg;        /* any degree on the score        */
  733. {
  734.     short *list;    /* current list of flats/sharps        */
  735.     short i;
  736.     short chrome;    /* chromatic modification of the key    */
  737.     int ret;        /* chromatic modification to return    */
  738.  
  739.     ret = CH_NAT;
  740.  
  741.     if (curkey.insharps) {
  742.     list = &trebsharps[0];
  743.     chrome = CH_SHARP;
  744.     } else {
  745.     list = &trebflats[0];
  746.     chrome = CH_FLAT;
  747.     }
  748.     for (i = 0; i < curkey.number; ++i) {
  749.     if (odegofdeg(deg) == odegofdeg(list[i])) {
  750.         ret = chrome;
  751.     }
  752.     }
  753.     return(ret);
  754. }
  755.  
  756. /*
  757.  * drawbits() - OR the given bitmap into the given point of the window.
  758.  */
  759.  
  760. drawbits(bmp, left, top)
  761. BitMap *bmp;        /* the bitmap to draw                */
  762. short left, top;    /* the top-left of where to put that bitmap    */
  763. {
  764.     Rect dstrect;
  765.     
  766.     topLeft(dstrect) = topLeft(bmp->bounds);
  767.     botRight(dstrect) = botRight(bmp->bounds);
  768.     OffsetRect(&dstrect, -dstrect.left + left, -dstrect.top + top);
  769.     CopyBits(bmp, &(thePort->portBits), &(bmp->bounds), &dstrect,
  770.       srcOr, (RgnHandle) 0);
  771. }
  772.  
  773. /*
  774.  * degtov() - convert from a degree number to the corresponding
  775.  * vertical coordinate.
  776.  */
  777.  
  778. short
  779. degtov(degree)
  780. {
  781.     return(scorerect.bottom - spaceheight -
  782.       (degree * spaceheight + 1) / 2);
  783. }
  784.  
  785. /*
  786.  * vtodeg() - convert from a vertical coord to a degree.
  787.  */
  788.  
  789. short
  790. vtodeg(v)
  791. short v;
  792. {
  793.     short dv;    /* distance from the point to the '0' degree    */
  794.     short deg;    /* the degree to return                */
  795.  
  796.     dv = scorerect.bottom - spaceheight - v;
  797.     deg = (dv * 2 + (spaceheight + 1) / 2) / spaceheight;
  798.  
  799.     if (deg < 0) {
  800.     deg = 0;
  801.     } else if (deg >= DEGPERSCORE) {
  802.     deg = DEGPERSCORE - 1;
  803.     }
  804.     return(deg);
  805. }
  806.  
  807. /*
  808.  * dvtochrome() - given a degree and a new vertical,
  809.  * return the chromatic change for that degree.
  810.  * Used to let the mouse control the chromatic change in a note.
  811.  */
  812.  
  813. short
  814. dvtochrome(deg, v)
  815. short deg;
  816. short v;
  817. {
  818.     short chrome;
  819.     int delta;
  820.  
  821.     /*
  822.      * Adjust the chromatic change only if accidentals are allowed.
  823.      */
  824.  
  825.     chrome = chromeofdeg(deg);
  826.     if (acc_ok) {
  827.     delta = ((scorerect.bottom - spaceheight - v) * 2 + (spaceheight + 1) / 2)
  828.       / spaceheight - deg;
  829.     chrome += delta;
  830.     }
  831.     if (chrome < CH_FLAT) {
  832.     chrome = CH_FLAT;
  833.     } else if (chrome > CH_SHARP) {
  834.     chrome = CH_SHARP;
  835.     }
  836.     return(chrome);
  837. }
  838.  
  839. /*
  840.  * paw() - get an array of pict bitmaps and their maximum width.
  841.  * I.E., given a pictinfo struct that has the resid[] array set,
  842.  * fill in its picts[] array and its 'width' field.
  843.  */
  844.  
  845. paw(infop)
  846. struct pictinfo *infop;
  847. {
  848.     short i;
  849.     PicHandle pih;        /* handle to this pict            */
  850.     short width;        /* width of this pict            */
  851.     BitMap *PictToMap();
  852.     
  853.     infop->width = 0;
  854.     for (i = 0; infop->resid[i]; ++i) {
  855.     pih = (PicHandle) GetResource((long) 'PICT', infop->resid[i]);
  856.     infop->bits[i] = PictToMap(pih);
  857.  
  858.     width = infop->bits[i]->bounds.right - infop->bits[i]->bounds.left;
  859.     if (infop->width < width) {
  860.         infop->width = width;
  861.     }
  862.     }
  863. }
  864.  
  865. /*
  866.  * cvrandspace() - Given a clef Bitmap,
  867.  * find the clef's offset (vref) and the score's line spacing (spaceheight).
  868.  *
  869.  * Do it by examining that bitmap to find where the lines are.
  870.  */
  871.  
  872. cvrandspace(bmp, vrp)
  873. BitMap *bmp;            /* pointer to the clef's bitmap        */
  874. short *vrp;            /* where to put the clef's vref        */
  875. {
  876.     GrafPtr saveport;
  877.     BitMap savebits;        /* current port's original bitmap    */
  878.     short v;            /* current vertical pixel being examined*/
  879.     
  880.     GetPort(&saveport);
  881.     SetPort(windoc);
  882.     savebits.baseAddr = thePort->portBits.baseAddr;
  883.     savebits.rowBytes = thePort->portBits.rowBytes;
  884.     topLeft(savebits.bounds) = topLeft(thePort->portBits.bounds);
  885.     botRight(savebits.bounds) = botRight(thePort->portBits.bounds);
  886.     SetPortBits(bmp);
  887.     
  888.     v = -1;
  889.     while (!GetPixel(0, ++v))    /* find the top of the first line    */
  890.     ;
  891.     *vrp = v;
  892.     
  893.     while (GetPixel(0, ++v))    /* skip past the bottom of that line    */
  894.     ;
  895.  
  896.     while (!GetPixel(0, ++v))    /* find the top of the next line    */
  897.     ;
  898.     
  899.     spaceheight = v - *vrp;
  900.     
  901.     SetPortBits(&savebits);
  902.     SetPort(saveport);
  903. }
  904.